package com.zhss.demo.concurrent;

/**
 * @author xuhua
 * @create 2021-11-29 20:37
 * @description double check单例模式的实现缺陷
 */
public class DoubleCheckSingleton {

    /**
     * 这里如果不加volatile则存在指令重排
     */
    public static volatile DoubleCheckSingleton instance;

    /**
     * 使用synchronized可以保证元素的可见性和原子性
     * 但是如果DoubleCheckSingleton中含有其他类型的实例
     * 类在实例化的时候会经历一个过程包含在内存中分配空间，指针指向内存空间等等过程
     * 在并发的场景下如果线程1调用getInstance方法来创建DoubleCheckSingleton
     * 此时线程1获取锁来是实例化DoubleCheckSingleton对象，但是由于该对象中含有普通对象soketInstance
     * 此时可能只是DoubleCheckSingleton对象实例化成功但是soketInstance没有实例化成功为空值情况下线程2又进来了
     * 此时线程2会从内存中去读取DoubleCheckSingleton实例，如果此时没有使用volatile关键字可能DoubleCheckSingleton
     * 实例化对象中soketInstance为空值，
     *
     * 加上volatile之后由于，volatile使用了storeload内存屏障从而使得线程1中DoubleCheckSingleton以及其内部类实例化全部完成
     * 线程2才能去读取实例化对象
     */
    private Object soketInstance;

    /**
     * 实例化soketInstance对象
     */
    public DoubleCheckSingleton() {
        this.soketInstance = soketInstance;
    }

    /**
     * 共享变量的可见性问题
     */
    public static DoubleCheckSingleton getInstance() {
        if (instance == null) {
            //  多个线程会卡在这
            synchronized (DoubleCheckSingleton.class) {
                // 有一个线程先进来
                // 第二个线程进来，此时如果没有这个 double check的判断的话，就会导致再次创建一个实例
                if (instance == null) {
                    // 创建一个单例
                    // synchronized本身可以保证可见性的问题，此时线程1创建对象之后会强制将对象刷入内存中
                    DoubleCheckSingleton.instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }

    public Object getSoketInstance() {
        return soketInstance;
    }

    public void setSoketInstance(Object soketInstance) {
        this.soketInstance = soketInstance;
    }
}
